---
type: integration
title: '@astrojs/cloudflare'
description: Learn how to use the @astrojs/cloudflare SSR adapter to deploy your Astro project.
githubIntegrationURL: 'https://github.com/withastro/adapters/tree/main/packages/cloudflare/'
category: adapter
i18nReady: true
---

import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'

This adapter allows Astro to deploy your [`hybrid` or `server` rendered site](/en/basics/rendering-modes/#on-demand-rendered) to [Cloudflare](https://www.cloudflare.com/).

If you're using Astro as a [static site builder](/en/basics/rendering-modes/#pre-rendered), you don't need an adapter.

Learn how to deploy your Astro site in our [Cloudflare Pages deployment guide](/en/guides/deploy/cloudflare/).

## Why Astro Cloudflare

[Cloudflare](https://www.cloudflare.com/) provides CDNs, web security, and other services. This adapter enhances the Astro build process to prepare your project for deployment through Cloudflare.

## Installation

Astro includes an `astro add` command to automate the setup of official integrations. If you prefer, you can [install integrations manually](#manual-install) instead.

Add the Cloudflare adapter to enable SSR in your Astro project with the `astro add` command. This will install `@astrojs/cloudflare` and make the appropriate changes to your `astro.config.mjs` file in one step.

<PackageManagerTabs>
  <Fragment slot="npm">
  ```sh
  npx astro add cloudflare
  ```
  </Fragment>
  <Fragment slot="pnpm">
  ```sh
  pnpm astro add cloudflare
  ```
  </Fragment>
  <Fragment slot="yarn">
  ```sh
  yarn astro add cloudflare
  ```
  </Fragment>
</PackageManagerTabs>

### Manual Install

First, add the `@astrojs/cloudflare` adapter to your project's dependencies using your preferred package manager.

<PackageManagerTabs>
  <Fragment slot="npm">
  ```sh
  npm install @astrojs/cloudflare
  ```
  </Fragment>
  <Fragment slot="pnpm">
  ```sh
  pnpm add @astrojs/cloudflare
  ```
  </Fragment>
  <Fragment slot="yarn">
  ```sh
  yarn add @astrojs/cloudflare
  ```
  </Fragment>
</PackageManagerTabs>

Then, add the adapter and your desired [on-demand rendering mode](/en/basics/rendering-modes/#on-demand-rendered) to your `astro.config.mjs` file:

```js title="astro.config.mjs" ins={2,5-6}
import { defineConfig } from 'astro/config';
import cloudflare from '@astrojs/cloudflare';

export default defineConfig({
  output: 'server',
  adapter: cloudflare(),
});
```

## Options

### imageService

<p>
**Type:** `'passthrough' | 'cloudflare' | 'compile'`<br />
**Default:** `'passthrough'`
</p>

Determines which image service is used by the adapter. The adapter will default to `passthrough` mode when an incompatible image service is configured. Otherwise, it will use the globally configured image service:

* **`cloudflare`:** Uses the [Cloudflare Image Resizing](https://developers.cloudflare.com/images/image-resizing/) service.
* **`passthrough`:** Uses the existing [`noop`](/en/guides/images/#configure-no-op-passthrough-service) service.
* **`compile`:** Uses Astro's default service (sharp), but only on pre-rendered routes at build time. During SSR for pages rendered on-demand, all `astro:assets` features are disabled.

```js title="astro.config.mjs" ins={6}
import {defineConfig} from "astro/config";
import cloudflare from '@astrojs/cloudflare';

export default defineConfig({
  adapter: cloudflare({
     imageService: 'cloudflare'
  }),
  output: 'server'
})
```

### platformProxy

Determines whether and how the Cloudflare runtime is added to `astro dev`. It contains proxies to local `workerd` bindings and emulations of Cloudflare specific values, allowing the emulation of the runtime in the Node.js dev process. Read more about the [Cloudflare Runtime](#cloudflare-runtime).

:::note
Proxies provided by this are a best effort emulation of the real production. Although they are designed to be as close as possible to the real thing, there might be a slight differences and inconsistencies between the two.
:::

#### platformProxy.enabled
<p>
**Type:** `{ enabled?: boolean }`<br />
**Default:** `{ enabled: false }`
</p>

The `enabled` property allows you to enable the Cloudflare runtime in `astro dev`.

#### platformProxy.configPath
<p>
**Type:** `{ configPath?: string }`<br />
**Default:** `{ configPath: 'wrangler.toml' }`
</p>

The `configPath` allows you to define your Wrangler configuration file, relative to the root of your Astro project. 

#### platformProxy.experimentalJsonConfig
<p>
**Type:** `{ experimentalJsonConfig?: boolean }`<br />
**Default:** `{ experimentalJsonConfig?: false }`
</p>

The `experimentalJsonConfig` property defines whether the utility reads a JSON config file (e.g. `wrangler.json`).

#### platformProxy.persist
<p>
**Type:** `{ persist?: boolean | { path: string } }`<br />
**Default:** `{ persist: true }`
</p>

The `persist` property defines if and where the bindings data is persistent. `true` defaults to the same location used by Wrangler so data can be shared between the two. If `false`, no data is persited to or read from the filesystem.

:::note
`wrangler`'s `--persist-to` option adds a sub directory called `v3` under the hood while the `@astrojs/cloudflare` `persist` property does not. For example, to reuse the same location as running `wrangler dev --persist-to ./my-directory`, you must specify: `persist: "./my-directory/v3"`.
:::

The following configuration shows an example of enabling the Cloudflare runtime when running the development server, as well as using a `wrangler.json` config file (experimental). It also specifies a custom location for persisting data to the filesystem:


```js
import cloudflare from '@astrojs/cloudflare';
import { defineConfig } from 'astro/config';

export default defineConfig({
	adapter: cloudflare({
		platformProxy: {
			enabled: true,
			configPath: 'wrangler.json',
			experimentalJsonConfig: true,
			persist: './.cache/wrangler/v3',
		},
	}),
});
```
### routes.extend

This option allows you to add or exclude custom patterns (e.g. `/fonts/*`) to the generated [`_routes.json`](#custom-_routesjson) file that determines which routes are generated on-demand. This can be useful if you need to add route patterns which cannot be automatically generated, or exclude prerendered routes.

More information about the custom route patterns can be found in [Cloudflare's routing docs](https://developers.cloudflare.com/pages/functions/routing/#functions-invocation-routes). Any routes specified are not automatically deduplicated and will be appended to the existing routes as is.

#### routes.extend.include

<p>
**Type:** `{ pattern: string }[]`<br />
**Default:** `undefined`
</p>

Configure additional routes to be generated on demand by the Cloudflare adapter in the `routes.extend.include` array.

#### routes.extend.exclude

<p>
**Type:** `{ pattern: string }[]`<br />
**Default:** `undefined`
</p>

Configure routes to be excluded from on-demand rendering in the `routes.extend.exclude` array. These routes will be prerendered and served statically instead, and will not invoke the SSR function. Additionally you can use this option to serve any static asset (e.g. images, fonts, css, js, html, txt, json, etc.) files directly without routing the request through the SSR function.

```js title="astro.config.mjs"
export default defineConfig({
  adapter: cloudflare({
    routes: {
      extend: {
        include: [{ pattern: '/static' }], // Route a prerended page to the SSR function for on-demand rendering
        exclude: [{ pattern: '/pagefind/*' }], // Use Starlight's pagefind search, which is generated statically at build time
      }
    },
  }),
});
```

### `wasmModuleImports`

<p>
**Type:** `true | false`<br />
**Default:** `false`
</p>

Whether or not to import `.wasm` files [directly as ES modules](https://github.com/WebAssembly/esm-integration/tree/main/proposals/esm-integration) using the `.wasm?module` import syntax.

Add `wasmModuleImports: true` to `astro.config.mjs` to enable this functionality in both `astro build` & `astro dev`. Read more about [using Wasm modules](#use-wasm-modules).

```js title="astro.config.mjs" ins={6}
import {defineConfig} from "astro/config";
import cloudflare from '@astrojs/cloudflare';

export default defineConfig({
  adapter: cloudflare({
     wasmModuleImports: true
  }),
  output: 'server'
})
```

## Cloudflare runtime

The Cloudflare runtime gives you access to environment variables and Cloudflare bindings.
The Cloudflare runtime uses bindings found in the `wrangler` and `.dev.vars` configuration files.

### Usage

For example, if you have an [environment variable](https://developers.cloudflare.com/workers/configuration/environment-variables/#add-environment-variables-via-wrangler) configuration set up in `wrangler.toml`:

```toml title="wrangler.toml"
[vars]
MY_VARIABLE = "test"
```

If you also need to define `secrets` in addition to environment variables, you need to add a `.dev.vars` file to the root of the Astro project:

```ini title=".dev.vars"
DB_PASSWORD=myPassword
```

You can access the bindings by using Astro locals like this:

```astro title="src/pages/index.astro"
---
const { env } = Astro.locals.runtime;
---
```

You can access the runtime from API endpoints through `context.locals`:

```js title="src/pages/api/someFile.js"
export function GET(context) {
  const runtime = context.locals.runtime;

  return new Response('Some body');
}
```

To access the value of the `MY_VARIABLE` binding add the following to your code:

```astro title="src/pages/index.astro"
---
const { env } = Astro.locals.runtime;
const myVariable = env.MY_VARIABLE;
---
```

See the [list of all supported bindings](https://developers.cloudflare.com/workers/wrangler/api/#supported-bindings) in the Cloudflare documentation.

### Typing

`wrangler` provides a `types` command to generate TypeScript types for the bindings. This allows you to type locals without the need to manually type them. Refer to the [Cloudflare documentation](https://developers.cloudflare.com/workers/wrangler/commands/#types) for more information.

Every time you change your configuration files (e.g. `wrangler.toml`, `.dev.vars`) you need to run `wrangler types`.

:::note
You can create a pnpm script to run `wrangler types` automatically before other commands.

```json title="package.json"
{
  "scripts": {
    "dev": "wrangler types && astro dev",
    "start": "wrangler types && astro dev",
    "build": "wrangler types && astro check && astro build",
    "preview": "wrangler types && astro preview",
    "astro": "astro"
  }
}
```
:::

You can type the `runtime` object using `Runtime`:

```ts title="src/env.d.ts"
/// <reference types="astro/client" />

type Runtime = import('@astrojs/cloudflare').Runtime<Env>;

declare namespace App {
  interface Locals extends Runtime {
    otherLocals: {
      test: string;
    };
  }
}
```

## Cloudflare Platform

### Headers

You can attach [custom headers](https://developers.cloudflare.com/pages/platform/headers/) to your responses by adding a `_headers` file in your Astro project's `public/` folder. This file will be copied to your build output directory.

### Assets
Assets built by Astro are all named with a hash and therefore can be given long cache headers. By default, Astro on Cloudflare will add such a header for these files.

### Redirects

You can declare [custom redirects](https://developers.cloudflare.com/pages/platform/redirects/) using Cloudflare Pages. This allows you to redirect requests to a different URL. You can add a `_redirects` file in your Astro project's `public/` folder. This file will be copied to your build output directory.

### Routes

[Cloudflare routing](https://developers.cloudflare.com/pages/platform/functions/routing/#functions-invocation-routes) uses a `_routes.json` file to determine which requests are routed to the SSR function and which are served as static assets. By default, a `_routes.json` file will be automatically generated for your project based on its files and configuration.

You can [specify additional routing patterns to follow](#routesextend) in your adapter config, or create your own custom `_routes.json` file to fully override the automatic generation.


#### Custom `_routes.json`

Creating a custom `public/_routes.json` will override the automatic generation. See [Cloudflare's documentation on creating a custom `_routes.json`](https://developers.cloudflare.com/pages/platform/functions/routing/#create-a-_routesjson-file) for more details.

## Use Wasm modules

The following is an example of importing a Wasm module that then responds to requests by adding the request's number parameters together.

```js title="pages/add/[a]/[b].js"
import mod from '../util/add.wasm?module';

// instantiate ahead of time to share module
const addModule: any = new WebAssembly.Instance(mod);

export async function GET(context) {
  const a = Number.parseInt(context.params.a);
  const b = Number.parseInt(context.params.b);
  return new Response(`${addModule.exports.add(a, b)}`);
}
```

While this example is trivial, Wasm can be used to accelerate computationally intensive operations which do not involve significant I/O such as embedding an image processing library.

## Node.js compatibility

Out of the box, Cloudflare does not support the Node.js runtime APIs. With some configuration, Cloudflare does support a subset of the Node.js runtime APIs. You can find supported Node.js runtime APIs in Cloudflare's [documentation](https://developers.cloudflare.com/workers/runtime-apis/nodejs).

To use these APIs, your page or endpoint must be server-side rendered (not pre-rendered) and must use the the `import {} from 'node:*'` import syntax.

```js title="pages/api/endpoint.js"
export const prerender = false;
import { Buffer } from 'node:buffer';
```

You'll also need to modify the `vite` configuration in your astro config to allow for the `node:*` import syntax:

```js title="astro.config.mjs" ins={7-11}
import {defineConfig} from "astro/config";
import cloudflare from '@astrojs/cloudflare';

export default defineConfig({
  adapter: cloudflare({}),
  output: 'server',
  vite: {
		ssr: {
			external: ['node:buffer'],
		},
	},
})
```

Additionally, you'll need to follow Cloudflare's documentation on how to enable support. For detailed guidance, please refer to the [Cloudflare documentation on enabling Node.js compatibility](https://developers.cloudflare.com/workers/runtime-apis/nodejs#enable-nodejs-with-pages-functions).

:::note[Package Compatibility Implications]
If a project imports a package into the server that uses the Node.js runtime APIs, this can cause issues when deploying to Cloudflare. This issue arises with package that do not use the `node:*` import syntax. It is recommended that you contact the authors of the package to determine if the package supports the above import syntax. If the package does not support this, you may need to use a different package.
:::

## Preview with Wrangler

To use [`wrangler`](https://developers.cloudflare.com/workers/wrangler/) to run your application locally, update the preview script:

```json title="package.json"
"preview": "wrangler pages dev ./dist"
```

[`wrangler`](https://developers.cloudflare.com/workers/wrangler/) gives you access to [Cloudflare bindings](https://developers.cloudflare.com/pages/platform/functions/bindings), [environment variables](https://developers.cloudflare.com/pages/platform/functions/bindings/#environment-variables), and the [cf object](https://developers.cloudflare.com/workers/runtime-apis/request/#incomingrequestcfproperties). Getting hot reloading or the astro dev server to work with Wrangler might require custom setup. See [community examples](https://github.com/withastro/roadmap/discussions/590).

### Meaningful error messages

Currently, errors during running your application in Wrangler are not very useful, due to the minification of your code. For better debugging, you can add `vite.build.minify = false` setting to your `astro.config.mjs`.

```js title="astro.config.mjs" ins={4-8}
export default defineConfig({
  adapter: cloudflare(),
  output: 'server',
  vite: {
    build: {
      minify: false,
    },
  },
});
```

[astro-integration]: /en/guides/integrations-guide/
